﻿package baseEngine.collisions
{
    import __AS3__.vec.*;
    import baseEngine.core.*;
    import baseEngine.utils.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.utils.*;
    import frEngine.*;
    import frEngine.core.*;
    import frEngine.event.*;
    import frEngine.primitives.*;

    public class RayCollision extends Object
    {
        public var data:Vector.<CollisionInfo>;
        private var _collided:Boolean;
        private var _collisionTime:int;
        private var _pull:Vector.<CollisionInfo>;
        private var _list:Vector.<Mesh3D>;
        private var _meshList:Dictionary;
        private static var _collisionDistance:Number;
        private static var _collisionSurface:Surface3D;
        private static var _collisionMesh:Mesh3D;
        private static var _collisionPoly:Poly3D;
        private static var _polyIntersectionNormal:Vector3D = new Vector3D();
        private static var _polyIntersectionPoint:Vector3D = new Vector3D();
        private static var _global:Matrix3D = new Matrix3D();
        private static var _inv:Matrix3D = new Matrix3D();
        private static var _pos:Vector3D = new Vector3D();
        private static var _dir:Vector3D = new Vector3D();
        private static var _q:Vector3D = new Vector3D();
        private static var _f:Vector3D = new Vector3D();
        private static var _d:Vector3D = new Vector3D();
        private static var _pIPoint:Vector3D = new Vector3D();
        private static var _dist0:Number;

        public function RayCollision()
        {
            this.data = new Vector.<CollisionInfo>;
            this._pull = new Vector.<CollisionInfo>;
            this._list = new Vector.<Mesh3D>;
            this._meshList = new Dictionary(true);
            return;
        }// end function

        public function dispose() : void
        {
            _collisionSurface = null;
            _collisionMesh = null;
            _collisionPoly = null;
            this._pull = null;
            this._list = null;
            this._meshList = null;
            return;
        }// end function

        public function test(param1:Vector3D, param2:Vector3D, param3:Boolean = false, param4:Boolean = true) : Boolean
        {
            var _loc_5:* = getTimer();
            while (this.data.length)
            {
                
                this._pull.push(this.data.pop());
            }
            this.update(param1, param2, param3, param4);
            if (!param3 && this._collided)
            {
                this.data.push(this.addInfo(_collisionMesh, _collisionSurface, _collisionPoly, _polyIntersectionPoint, _polyIntersectionNormal));
            }
            this._collisionTime = getTimer() - _loc_5;
            return this._collided;
        }// end function

        private function update(param1:Vector3D, param2:Vector3D, param3:Boolean = false, param4:Boolean = false) : void
        {
            var _loc_5:Mesh3D = null;
            var _loc_6:Poly3D = null;
            var _loc_7:FrSurface3D = null;
            var _loc_8:Vector3D = null;
            var _loc_9:Number = NaN;
            var _loc_10:Number = NaN;
            var _loc_11:Number = NaN;
            var _loc_12:Vector.<Poly3D> = null;
            var _loc_13:int = 0;
            var _loc_14:int = 0;
            var _loc_15:int = 0;
            var _loc_19:int = 0;
            var _loc_20:int = 0;
            var _loc_21:Number = NaN;
            var _loc_22:Number = NaN;
            this._collided = false;
            _collisionDistance = Number.MAX_VALUE;
            var _loc_16:* = Number.MAX_VALUE;
            var _loc_17:* = Number.MAX_VALUE;
            var _loc_18:* = new Vector3D();
            for each (_loc_5 in this._list)
            {
                
                if (_loc_5.visible && _loc_5.mouseEnabled)
                {
                    _global.copyFrom(_loc_5.world);
                    Matrix3DUtils.invert(_global, _inv);
                    Matrix3DUtils.transformVector(_inv, param1, _f);
                    Matrix3DUtils.deltaTransformVector(_inv, param2, _d);
                    _d.normalize();
                    if (_loc_5.bounds)
                    {
                        _loc_8 = _loc_5.bounds.center;
                        _loc_9 = _loc_5.bounds.radius;
                        _q.x = _f.x - _loc_8.x;
                        _q.y = _f.y - _loc_8.y;
                        _q.z = _f.z - _loc_8.z;
                        _loc_10 = _q.dotProduct(_d);
                        _loc_11 = _q.dotProduct(_q) - _loc_9 * _loc_9;
                        if (_loc_10 * _loc_10 - _loc_11 < 0)
                        {
                            continue;
                        }
                    }
                    _loc_19 = _loc_5.getSurfacesLen();
                    _loc_20 = 0;
                    while (_loc_20 < _loc_19)
                    {
                        
                        _loc_7 = _loc_5.getSurface(_loc_20);
                        _loc_12 = _loc_7.polys;
                        if (!_loc_12)
                        {
                            return;
                        }
                        if (param4 == true && _loc_5.material && _loc_5.material.materialParams.twoSided == true)
                        {
                            param4 = false;
                        }
                        _loc_13 = _loc_7.firstIndex / 3;
                        _loc_14 = _loc_7.indexBufferFr.numTriangles;
                        if (_loc_14 == -1)
                        {
                            _loc_14 = _loc_7.polys.length;
                        }
                        _loc_14 = _loc_14 + _loc_13;
                        _loc_15 = _loc_13;
                        while (_loc_15 < _loc_14)
                        {
                            
                            _loc_6 = _loc_12[_loc_15];
                            if (!(param4 && _loc_6.normal.x * _f.x + _loc_6.normal.y * _f.y + _loc_6.normal.z * _f.z + _loc_6.plane < 0))
                            {
                                _loc_21 = _loc_6.normal.x * _f.x + _loc_6.normal.y * _f.y + _loc_6.normal.z * _f.z + _loc_6.plane;
                                _loc_22 = _loc_6.normal.x * _d.x + _loc_6.normal.y * _d.y + _loc_6.normal.z * _d.z;
                                _dist0 = (-_loc_21) / _loc_22;
                                if (_dist0 > 0)
                                {
                                    _pIPoint.x = _f.x + _d.x * _dist0;
                                    _pIPoint.y = _f.y + _d.y * _dist0;
                                    _pIPoint.z = _f.z + _d.z * _dist0;
                                    if (_loc_6.isPoint(_pIPoint.x, _pIPoint.y, _pIPoint.z))
                                    {
                                        Matrix3DUtils.transformVector(_global, _pIPoint, _loc_18);
                                        _loc_17 = Vector3D.distance(param1, _loc_18);
                                        _collisionDistance = _dist0;
                                        this._collided = true;
                                        if (_loc_17 < _loc_16 || param3)
                                        {
                                            _collisionPoly = _loc_6;
                                            _collisionSurface = _loc_7;
                                            _collisionMesh = _loc_5;
                                            Matrix3DUtils.deltaTransformVector(_global, _loc_6.normal, _polyIntersectionNormal);
                                            _polyIntersectionPoint.copyFrom(_loc_18);
                                            if (param3)
                                            {
                                                if (_loc_17 < _loc_16)
                                                {
                                                    _loc_16 = _loc_17;
                                                    this.data.unshift(this.addInfo(_loc_5, _loc_7, _loc_6, _polyIntersectionPoint, _polyIntersectionNormal));
                                                }
                                                else
                                                {
                                                    this.data.push(this.addInfo(_loc_5, _loc_7, _loc_6, _polyIntersectionPoint, _polyIntersectionNormal));
                                                }
                                            }
                                            else
                                            {
                                                _loc_16 = _loc_17;
                                            }
                                        }
                                    }
                                }
                            }
                            _loc_15++;
                        }
                        _loc_20++;
                    }
                }
            }
            return;
        }// end function

        private function addInfo(param1:Mesh3D, param2:Surface3D, param3:Poly3D, param4:Vector3D, param5:Vector3D) : CollisionInfo
        {
            var _loc_6:* = this._pull.length ? (this._pull.pop()) : (new CollisionInfo());
            _loc_6.mesh = param1;
            _loc_6.surface = param2;
            _loc_6.poly = param3;
            _loc_6.point.copyFrom(param4);
            _loc_6.normal.copyFrom(param5);
            _loc_6.u = param3.getPointU();
            _loc_6.v = param3.getPointV();
            return _loc_6;
        }// end function

        public function addCollisionWith(param1:Pivot3D, param2:Boolean = true) : void
        {
            var _loc_3:Mesh3D = null;
            var _loc_4:FrSurface3D = null;
            var _loc_5:Pivot3D = null;
            var _loc_7:int = 0;
            var _loc_8:int = 0;
            var _loc_6:* = param1 is FrPlane ? (FrPlane(param1).offsetMatrix) : (null);
            if (this._meshList[param1] == undefined)
            {
                _loc_3 = param1 as Mesh3D;
                if (_loc_3)
                {
                    _loc_7 = _loc_3.getSurfacesLen();
                    _loc_8 = 0;
                    while (_loc_8 < _loc_7)
                    {
                        
                        _loc_4 = _loc_3.getSurface(_loc_8);
                        if (!_loc_4.polys)
                        {
                            Surface3DUtils.buildPolys(_loc_4, _loc_6);
                        }
                        _loc_8++;
                    }
                    FrEventDispatcher.instance.proxyAddEventListener(_loc_3, Engine3dEventName.UNLOAD_EVENT, this.unloadEvent, false, 0, true);
                    FrEventDispatcher.instance.proxyAddEventListener(_loc_3, Engine3dEventName.REMOVED_FROM_SCENE_EVENT, this.unloadEvent, false, 0, true);
                    this._meshList[_loc_3] = this._list.push(_loc_3) - 1;
                }
            }
            if (param2)
            {
                for each (_loc_5 in param1.children)
                {
                    
                    this.addCollisionWith(_loc_5, param2);
                }
            }
            return;
        }// end function

        private function unloadEvent(event:Event) : void
        {
            while (this.data.length)
            {
                
                this._pull.push(this.data.pop());
            }
            this.removeCollisionWith(event.target as Pivot3D, false);
            return;
        }// end function

        public function removeCollisionWith(param1:Pivot3D, param2:Boolean = true) : void
        {
            var _loc_3:Mesh3D = null;
            var _loc_4:uint = 0;
            var _loc_5:Pivot3D = null;
            if (this._meshList[param1] >= 0)
            {
                if (param1 is Mesh3D)
                {
                    _loc_3 = param1 as Mesh3D;
                    _loc_4 = this._list.indexOf(_loc_3);
                    delete this._meshList[_loc_3];
                    this._list.splice(_loc_4, 1);
                }
            }
            if (param2)
            {
                for each (_loc_5 in param1.children)
                {
                    
                    this.removeCollisionWith(_loc_5, param2);
                }
            }
            return;
        }// end function

        public function get collisionTime() : int
        {
            return this._collisionTime;
        }// end function

        public function get collisionCount() : int
        {
            return this._list.length;
        }// end function

        public function get collided() : Boolean
        {
            return this._collided;
        }// end function

    }
}
